#ifndef __YUECHUAN_FIBER_H__
#define __YUECHUAN_FIBER_H__

#include <memory>
#include <ucontext.h>
#include "thread.h"

namespace yuechuan {
    /**
     * @brief 协程类
     * @details 每个线程中都有一个main协程，所有的协程切换都是和这个main协程进行交互的
     *
     * 继承与enable_shared_from_this,该类的子类会继承一个shared_from_this的成员函数，
				当创建指向该类对象的指针指针pt时，可以调用Fiber::shared_from_this来获得里另外一个智能指针pr
				则pr和pt可以共享资
     */
    class Fiber : public std::enable_shared_from_this<Fiber> {
        friend class Scheduler;                         /// 信号量友元

    public:
        typedef std::shared_ptr<Fiber> ptr;

        enum State                                                    // 协程状态
        {
            INIT,                                           /// 初始化状态
            HOLD,                                           ///  暂停
            EXEC,                                           /// 运行中
            TERM,                                           /// 结束
            READY,                                          /// 就绪
            EXCE                                            /// 异常
        };

        /**
         * @brief 含参构造函数，参数为协程默认执行的回调函数，后一个参数是该协程要分配的栈的大小
         * @param cb 协程的回调函数
         * @param stackSize 协程调用栈的大小
         * @param user_caller 是否使用
         */
        Fiber(std::function<void()> cb, size_t stackSize = 0, bool user_caller = false);

        /**
         * @brief 析构函数，释放资源
         */
        ~Fiber();

        /**
         * @brief 重置该协程状态，回调函数
         * @details 需要重置的协程状态为INIT、TERM
         * @param cb 重置的回调函数
         */
        void reset(std::function<void()> cb);

        /**
         * @brief 强制切换当前线程的主协程为当前协程
         */
        void call();

        /**
         * @brief 切换当前协程为当前线程的主协程
         */
        void back();

        /**
         * @brief 协程调度器的主协程和当前协程进行切换
         * @details 将当前协程切换到前台执行
         */
        void swapIn();

        /**
         * @brief 将当前协程和协程调度器的主协程进行切换
         * @details 将当前协程切换到后台
         */
        void swapOut();

        /**
         * @brief 获取协程id
         * @return uint64_t m_id
         */
        uint64_t getId() const { return m_id; }

        /**
         * @brief 获取协程状态
         * @return State m_state
         */
        State getState() const { return m_state; };

        /**
         * @brief 获取当前协程
         * @return Fiber
         */
        static Fiber::ptr GetThis();

        /**
         * @brief 设置传入的协程为主协程
         * @param fb 要设置为主协程的协程指针
         */
        static void SetThis(Fiber *fb);

        /**
         * @brief 切换协程到后台，并设置为Ready状态
         */
        static void YieldToReady();

        /**
         * @brief 切换协程到后台，并设置为Hold状态
         */
        static void YieldToHold();

        /**
         * @brief 获取总的协程数目
         * @return uint64_t
         */
        static uint64_t TotalFibers();

        /**
         * @brief 默认的执行函数，可以作为参数传递给新创建的协程
         */
        static void MainFunc();

        static void CallerMainFunc();

        static uint64_t GetFiberId();

    private:
        /**
         * @brief 私有无参构造函数
         * @details 仅仅能在静态函数中调用，这样可以保证其唯一性，作为主协程
         */

        Fiber();

        uint64_t m_id = 0;                                             /// 协程ID
        uint32_t m_stackSize = 0;                                     /// 创建的栈的大小
        State m_state = INIT;                                        /// 默认状态
        ucontext_t m_ctx;                                            /// 上线文管理对象
        void *m_stack = nullptr;                                     /// 默认的栈指针

        std::function<void()> m_cb;                                  /// 回调函数
    };
} // namespace yuechuan

#endif
